package com.zhb.cms.domain.service;

import java.net.URI;
import java.util.Base64;
import java.util.Date;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.security.oauth2.common.OAuth2AccessToken;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.zhb.cms.domain.dao.UrUserLoginInfoMapper;
import com.zhb.cms.domain.vo.UserLoginVO;
import com.zhb.cms.domain.vo.UserTokenVO;
import com.zhb.cms.sdk.bean.Result;
import com.zhb.cms.sdk.exception.ErrorHandler;
import com.zhb.cms.sdk.util.SecurityUtil;

@Service("userLoginService")
@ConfigurationProperties(prefix = "spring.redis")
public class UserLoginService {
	
	private long lockLoginTime;//获取配置的超时时间	
	private long errorTimesTime; //监测错误次数的有效时间，单位分钟
	private String oAuthUrl;//获取oauth的配置服务器地址	
	@Autowired
	private UrUserLoginInfoMapper urUserLoginInfoMapper;

	@Autowired
	@Qualifier("userRedisTemplate")
	private RedisTemplate<String, String> userRedisTemplate;
	/**
	 * 
	* @Title: login 
	* @Description: 通用内部用户登录服务 
	* @author  张海滨  zhanghb22 
	* @date 2017年12月9日 下午10:15:48 
	* @return Result<UserTokenVO>    返回类型
	 */
	public Result<UserTokenVO> login(UserLoginVO userLoginVO){
		   //一、获取该类型下账号登录错误次数
		   int errorCount = errorCount(userLoginVO);
		   if(errorCount>=10){
			  //登录错误次数连续超过10次，账户锁定，请及时重置密码或联系相关管理员
			   return new Result<UserTokenVO>("userlogin001_01");
		    }
		 //二、登录错误次数超过5次启动图片验证码验证
			if(errorCount>=6){
				Boolean validate = validate(userLoginVO, errorCount);
				if(validate==null){
					//登录验证码验证超时
					return new Result<UserTokenVO>("userlogin001_02");
				}
				if(!validate){
					//登录验证码不正确
					return new Result<UserTokenVO>("userlogin001_03");
				}
			}
			//三、对密码md5加密
			userLoginVO.setPassword(SecurityUtil.MD5(userLoginVO.getPassword()));
			//四、通过ss验证用户名密码并获取token
			OAuth2AccessToken token;
			try {		

				token = generateToken(userLoginVO.getLoginName(),userLoginVO.getTypeCd(),userLoginVO.getPassword(),"appClient","secret");
			} catch (Exception e) {
				//用户不存在或者密码错误
				ValueOperations<String, String> opsForValue = userRedisTemplate.opsForValue();
				opsForValue.set("error_"+userLoginVO.getTypeCd()+"_" +userLoginVO.getLoginName() , String.valueOf(errorCount+1));
				userRedisTemplate.expire("error_"+userLoginVO.getTypeCd()+"_" +userLoginVO.getLoginName(), this.errorTimesTime, TimeUnit.MINUTES);
				return new Result<UserTokenVO>("userlogin001_04");
			}
			//五、查询返回请求者用户信息
			UserTokenVO userTokenVO=getUserLoginInfo(userLoginVO);					
			//六、缓存请求者token信息
			userTokenVO.setToken(token.getValue());
			saveToken(token,userTokenVO.getUserCd(),userTokenVO,userLoginVO.getTypeCd());					
			//七、返回请求者令牌等信息
			return new Result<UserTokenVO>("0",null,userTokenVO);							
	}
	/**
	 * 
	* @Title: errorCount 
	* @Description: 获取同一登录类型下登录账号的登录错误次数 
	* @author  张海滨  zhanghb22 
	* @date 2017年12月9日 下午10:24:17 
	* @return int    返回类型
	 */
	public int errorCount(UserLoginVO userLoginVO){
		ValueOperations<String, String> opsForValue = userRedisTemplate.opsForValue();
		String errorLoginInfo = opsForValue.get("error_" + userLoginVO.getTypeCd() + "_" + userLoginVO.getLoginName());
		int count = 0;
		if(errorLoginInfo!=null&&!"".equals(errorLoginInfo)){
			count=Integer.parseInt(errorLoginInfo);
		}
		return count;
	}
	/**
	 * 
	* @Title: validate 
	* @Description:  校验图片验证码服务
	* @author  张海滨  zhanghb22 
	* @date 2017年12月9日 下午10:55:11 
	* @return Boolean    返回类型
	 */
	public Boolean validate(UserLoginVO userLoginVO,int errorCount){
		ValueOperations<String, String> opsForValue = userRedisTemplate.opsForValue();
		String validate = opsForValue.get("validateCode_"+userLoginVO.getValId());
		if(validate==null||"".equals(validate)){
			return null;
		}
		if(!validate.equalsIgnoreCase(userLoginVO.getValCode())){
			userRedisTemplate.delete("validateCode_"+userLoginVO.getValId());
			return false;
		}
		return true;
	}
	/**
	 * 
	* @Title: getFromOAuth 
	* @Description: 生成token服务 
	* @author  张海滨  zhanghb22 
	* @date 2017年12月9日 下午11:45:47 
	* @return OAuth2AccessToken    返回类型
	 */
	public OAuth2AccessToken generateToken(String username,String typeCd,String password,String clientId,String clientSecret){
		RestTemplate restTemplate=new RestTemplate();
		HttpHeaders header = new HttpHeaders();
		String auth = clientId + ":" + clientSecret;
		byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
		String authHeader = "Basic " + new String(encodedAuth);
		header.set(HttpHeaders.AUTHORIZATION, authHeader);
		header.set(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded; charset=utf-8");
		String body = "grant_type=password&username="+ username+"_"+typeCd + "&password=" + password+"&client_id="+clientId+"&client_secret="+clientSecret;
		RequestEntity<String> request = new RequestEntity<String>(body, header, HttpMethod.POST, URI.create(oAuthUrl+"/oauth/oauth/token"));
		ResponseEntity<OAuth2AccessToken> result = restTemplate.exchange(request, OAuth2AccessToken.class);
		OAuth2AccessToken oauth = result.getBody();
		userRedisTemplate.delete("error_"+typeCd+"_" + username);
		return oauth;
	}
	/**
	 * 
	* @Title: saveToken 
	* @Description: 缓存用户token到redis 
	* @author  张海滨  zhanghb22 
	* @date 2017年12月10日 上午1:03:03 
	* @return String    返回类型
	 */
	public boolean saveToken(OAuth2AccessToken token,String userCd,Object userToken,String typeCd){
		ValueOperations<String, String> opsForValue = userRedisTemplate.opsForValue();
		//String encode = SecurityUtil.MD5(token.getValue());
		System.out.println("redis的key:"+token+"_"+userCd);
		Set<String> keys = userRedisTemplate.keys("*_" + userCd+"_"+typeCd);
		userRedisTemplate.delete(keys);
		opsForValue.set(token+"_"+userCd+"_"+typeCd, JSON.toJSONString(userToken,SerializerFeature.WriteMapNullValue));
		//设置缓存时间
		if(token.getExpiration()!=null){
			userRedisTemplate.expire(token+"_"+userCd+"_"+typeCd, token.getExpiration().getTime()-new Date().getTime(), TimeUnit.MILLISECONDS);
		}else{
			userRedisTemplate.expire(token+"_"+userCd+"_"+typeCd, this.lockLoginTime, TimeUnit.HOURS);
		}
		return true;
	}
	/**
	 * 
	* @Title: getUserLoginInfo 
	* @Description: 获取用户登录信息 
	* @author  张海滨  zhanghb22 
	* @date 2017年12月10日 上午1:25:11 
	* @return UserTokenVO    返回类型
	 */
	public UserTokenVO getUserLoginInfo(UserLoginVO userLoginVO) {
		UserTokenVO userTokenvo=urUserLoginInfoMapper.getUserLoginInfo(userLoginVO);
		return userTokenvo;
	}
	/**
	 * 
	* @Title: loginOut 
	* @Description: 用户登出服务 
	* @author  张海滨  zhanghb22 
	* @date 2017年12月14日 下午3:22:18 
	* @return int    返回类型
	 */
	public Result<String> loginOut(String token){
		Set<String> keys=userRedisTemplate.keys(token+"_*");
		if(keys.isEmpty()){
			ErrorHandler.reportError("userlogin002_01");
		}
		userRedisTemplate.delete(keys);
		return new Result<String>("0","登录退出成功",null);
	}
	
	
	public long getLockLoginTime() {
		return lockLoginTime;
	}
	public void setLockLoginTime(long lockLoginTime) {
		this.lockLoginTime = lockLoginTime;
	}
	public long getErrorTimesTime() {
		return errorTimesTime;
	}
	public void setErrorTimesTime(long errorTimesTime) {
		this.errorTimesTime = errorTimesTime;
	}
	public String getoAuthUrl() {
		return oAuthUrl;
	}

	public void setoAuthUrl(String oAuthUrl) {
		this.oAuthUrl = oAuthUrl;
	}
}
